/**
 * Created with JetBrains WebStorm.
 * User: Ender
 * Date: 6/2/13
 * Time: 7:04 PM
 * To change this template use File | Settings | File Templates.
 */

'use strict';

(function () {

    var terrainTypes = {
        water: 'water',
        trap: 'trap',
        den: 'den',
        grass: 'grass'
    };

    var cellHighlights = {
        selectedAnimal: 'selectedAnimal',
        attackShadow: 'attackShadow',
        moveShadow: 'moveShadow'
    };

    // animals also use the rules, so it needs to be global
    var ruleValues = {};

    angular.module('ender.boardgames.junglechess', [])
        .factory('jungleChessGame', function () {
            var customRules = [
                {
                    id: "elephantMayKillRat",
                    name: "The elephant may kill the rat",
                    type: "boolean",
                    default: true
                },
                {
                    id: "tigerEqualsLion",
                    name: "The lion and the tiger are equally strong (the lion is stronger otherwise)",
                    type: "boolean",
                    default: false
                },
                {
                    id: "leopardJump",
                    name: "The leopard may jump across water horizontally",
                    type: "boolean",
                    default: true
                },
                {
                    id: "lionJump",
                    name: "The lion may jump across water horizontally",
                    type: "boolean",
                    default: true
                },
                {
                    id: "universalTraps",
                    name: "Traps are universal for both sides",
                    type: "boolean",
                    default: false
                },
                {
                    id: "foxReplacesWolf",
                    name: "Fox replaces wolf",
                    type: "boolean",
                    default: false
                },
                {
                    id: "attackFromWater",
                    name: "Animals may attack when entering/exiting water",
                    type: "boolean",
                    default: false
                },
                {
                    id: "highLevelAnimalsMayEnterTraps",
                    name: "High level animals (5-8) may enter the traps",
                    type: "boolean",
                    default: true
                },
                {
                    id: "dogCanSwim",
                    name: "Dog can swim",
                    type: "boolean",
                    default: false
                }
            ];

            var boardSize = {
                height: 9,
                width: 7
            };

            function setupAnimals(ruleValues) {
                var animals = [ new Animal('Rat', 2, 0, 0), new Animal('Cat', 1, 5, 1), new Animal('Leopard', 2, 2, 4), new Animal('Lion', 0, 0, 6),
                    new Animal('Elephant', 2, 6, 7) ];
                {
                    var tiger = new Animal('Tiger', 0, 6);
                    tiger.powerLevel = ruleValues.tigerEqualsLion ? 6 : 5;
                    animals.push(tiger);
                }
                {
                    var dog = new Animal('Dog', 1, 1);
                    var wolf = new Animal('Wolf', 2, 4);
                    var fox = new Animal('Fox', 2, 4);
                    if (ruleValues.foxReplacesWolf) {
                        fox.powerLevel = 2;
                        dog.powerLevel = 3;

                        animals.push(dog);
                        animals.push(fox);
                    } else {
                        dog.powerLevel = 2;
                        wolf.powerLevel = 3;

                        animals.push(dog);
                        animals.push(wolf);
                    }
                }
                return animals;
            }


            function unhighlightCell(board, rowIndex, columnIndex) {
                var cell = board[rowIndex][columnIndex];
                cell.highlight = '';

                var previousNeighbors = getNeighbors(board, rowIndex, columnIndex);
                for (var i = 0; i < previousNeighbors.length; i++) {
                    // skip out of bounds indexes
                    if (!previousNeighbors[i])
                        continue;
                    previousNeighbors[i].highlight = '';
                }
            }

            function getNeighbors(board, rowIndex, columnIndex) {
                // left and right
                var neighbors = [ board[rowIndex][columnIndex - 1], board[rowIndex][columnIndex + 1] ];
                // up
                if (board[rowIndex - 1])
                    neighbors.push(board[rowIndex - 1][columnIndex]);
                // down
                if (board[rowIndex + 1])
                    neighbors.push(board[rowIndex + 1][columnIndex]);
                // across water : right
                var nextColumnIndex = columnIndex - 1;
                while (board[rowIndex][nextColumnIndex] && board[rowIndex][nextColumnIndex].type == terrainTypes.water) {
                    nextColumnIndex--;
                }
                if (nextColumnIndex != columnIndex - 1)
                    neighbors.push(board[rowIndex][nextColumnIndex]);
                // across water : left
                nextColumnIndex = columnIndex + 1;
                while (board[rowIndex][nextColumnIndex] && board[rowIndex][nextColumnIndex].type == terrainTypes.water) {
                    nextColumnIndex++;
                }
                if (nextColumnIndex != columnIndex + 1)
                    neighbors.push(board[rowIndex][nextColumnIndex]);
                // across water : up
                var nextRowIndex = rowIndex - 1;
                while (board[nextRowIndex] && board[nextRowIndex][columnIndex] && board[nextRowIndex][columnIndex].type == terrainTypes.water) {
                    nextRowIndex--;
                }
                if (nextRowIndex != rowIndex - 1)
                    neighbors.push(board[nextRowIndex][columnIndex]);
                // across water : down
                nextRowIndex = rowIndex + 1;
                while (board[nextRowIndex] && board[nextRowIndex][columnIndex] && board[nextRowIndex][columnIndex].type == terrainTypes.water) {
                    nextRowIndex++;
                }
                if (nextRowIndex != rowIndex + 1)
                    neighbors.push(board[nextRowIndex][columnIndex]);
                // result
                return neighbors;
            }

            function canAttack(place, destination, board) {
                var attacker = place.animal, defender = destination.animal;
                // can't move on ally
                if (attacker.player == defender.player)
                    return false;
                // can't attack when exiting or entering water
                if (!ruleValues.attackFromWater) {
                    if (place.type != destination.type && (place.type == terrainTypes.water || destination.type == terrainTypes.water))
                        return false;
                }
                // check jumping
                var distance = boardMath.lineDistance(place, destination);
                if (distance > 1) {
                    return attacker.canJump(distance) && attacker.canKill(defender) && cellsAreFreeBetween(place, destination, board);
                }
                // if the enemy is on your trap, it can be attacked by anything
                // alternate rule: traps are universal
                if (destination.type == terrainTypes.trap && (ruleValues.universalTraps || destination.player == place.animal.player)) {
                    return true;
                }
                // regular attack
                return attacker.canKill(defender);
            }

            function cellsAreFreeBetween(place, destination, board) {
                if (place.x == destination.x) {
                    var beginY = place.y < destination.y ? place.y : destination.y;
                    var endY = place.y > destination.y ? place.y : destination.y;
                    for (var i = beginY + 1; i < endY - 1; i++) {
                        if (board[place.x][i].animal)
                            return false;
                    }
                    return true;
                } else if (place.y == destination.y) {
                    var beginX = place.x < destination.x ? place.x : destination.x;
                    var endX = place.x > destination.x ? place.x : destination.x;
                    for (var i = beginX + 1; i < endX - 1; i++) {
                        if (board[i][place.y].animal)
                            return false;
                    }
                    return true;
                } else
                    throw "You can't get here: free cells";
            }

            function canMove(place, destination, board) {
                var distance = boardMath.lineDistance(place, destination);
                if (distance > 1) {
                    return place.animal.canJump(distance) && cellsAreFreeBetween(place, destination, board);
                }
                // only the rat can swim
                if (destination.type == terrainTypes.water)
                    return place.animal.canSwim();
                // can't move into own den
                if (destination.type == terrainTypes.den)
                    return place.animal.player != destination.player;
                // can move freely in traps or grass
                if (destination.type == terrainTypes.grass)
                    return true;
                if (destination.type == terrainTypes.trap) {
                    if (ruleValues.highLevelAnimalsMayEnterTraps) {
                        return true;
                    } else {
                        return place.animal.powerLevel < 4;
                    }
                }
                throw "You can't get here: canMove";
            }

            function evaluateMovement(cell, destination, board) {
                if (destination === undefined)
                    return;

                if (destination.animal) {
                    if (canAttack(cell, destination, board)) {
                        destination.highlight = cellHighlights.attackShadow;
                    }
                } else {
                    if (canMove(cell, destination, board)) {
                        destination.highlight = cellHighlights.moveShadow;
                    }
                }
            }

            var moveHasBeenMade = false;

            return {
                name: "Jungle Chess",
                customRules: customRules,
                boardSize: boardSize,
                movesNeedMultipleTurns: true,
                preProcessBoard: function (board, players) {
                    // set all cell to empty grass
                    for (var i = 0; i < board.length; i++) {
                        var row = board[i];
                        for (var j = 0; j < row.length; j++) {
                            var cell = row[j];
                            angular.extend(cell, {
                                type: terrainTypes.grass,
                                x: i,
                                y: j,
                                highlight: ''
                            })
                        }
                    }
                    // set terrain
                    var waterCells = [
                        [ 3, 1 ],
                        [ 3, 2 ],
                        [ 3, 4 ],
                        [ 3, 5 ],
                        [ 4, 1 ],
                        [ 4, 2 ],
                        [ 4, 4 ],
                        [ 4, 5 ],
                        [ 5, 1 ],
                        [ 5, 2 ],
                        [ 5, 4 ],
                        [ 5, 5 ]
                    ];
                    var player1TrapCells = [
                        [ 0, 2 ],
                        [ 0, 4 ],
                        [ 1, 3 ]
                    ];
                    var player2TrapCells = [
                        [ 7, 3 ],
                        [ 8, 2 ],
                        [ 8, 4 ]
                    ];
                    var denCells = [
                        [ 0, 3 ],
                        [ 8, 3 ]
                    ];
                    // set water
                    for (var i = 0; i < waterCells.length; i++) {
                        board[waterCells[i][0]][waterCells[i][1]].type = terrainTypes.water;
                    }
                    // set traps
                    for (var i = 0; i < player1TrapCells.length; i++) {
                        var trapCell = board[player1TrapCells[i][0]][player1TrapCells[i][1]];
                        trapCell.type = terrainTypes.trap;
                        trapCell.player = players[0];
                    }
                    for (var i = 0; i < player2TrapCells.length; i++) {
                        var trapCell = board[player2TrapCells[i][0]][player2TrapCells[i][1]];
                        trapCell.type = terrainTypes.trap;
                        trapCell.player = players[1];
                    }
                    // set den
                    for (var i = 0; i < denCells.length; i++) {
                        var denCell = board[denCells[i][0]][denCells[i][1]];
                        denCell.type = terrainTypes.den;
                        denCell.player = players[i];
                        players[i].den = denCell;
                    }

                    var animals = setupAnimals(ruleValues);
                    var rows = this.boardSize.height;
                    var columns = this.boardSize.width;

                    players[0].animalsCount = players[1].animalsCount = animals.length;

                    for (var j = 0; j < animals.length; j++) {
                        var animalPlayer1 = angular.copy(animals[j]);
                        board[animalPlayer1.x][animalPlayer1.y].animal = animalPlayer1;
                        animalPlayer1.player = players[0];

                        var animalPlayer2 = angular.copy(animals[j]);
                        animalPlayer2.x = rows - 1 - animals[j].x;
                        animalPlayer2.y = columns - 1 - animals[j].y;
                        board[animalPlayer2.x][animalPlayer2.y].animal = animalPlayer2;
                        animalPlayer2.player = players[1];
                    }
                },
                processRules: function (rules) {
                    rules = rules || customRules;

                    for (var i = 0; i < rules.length; i++) {
                        var gameRule = rules[i];
                        ruleValues[gameRule.id] = gameRule.value || gameRule.default;
                    }
                },
                processPlayers: function (players) {
                    players[0].gameName = 'top-player';
                    players[1].gameName = 'bottom-player';
                },
                checkVictory: function (board, players, currentPlayer) {
                    for (var i = 0; i < players.length; i++) {
                        if (players[i] !== currentPlayer) {
                            var otherPlayer = players[i];

                            // other player's den is free
                            // other player still has animals
                            return otherPlayer.den.animal || otherPlayer.animals === 0;
                        }
                    }
                },
                processCellClicked: function (board, row, column, currentPlayer) {
                    moveHasBeenMade = false;
                    var cell = board[row][column];
                    // if own animal selected
                    if (cell.animal && cell.animal.player === currentPlayer) {

                        if (currentPlayer.previousSelection) {
                            // same animal selected
                            if (currentPlayer.previousSelection === cell)
                                return;
                            else {
                                // un-highlight previous selection
                                var previousRow = currentPlayer.previousSelection.animal.x;
                                var previousColumn = currentPlayer.previousSelection.animal.y;
                                unhighlightCell(board, previousRow, previousColumn);
                            }
                        }

                        // highlight animal
                        cell.highlight = cellHighlights.selectedAnimal;

                        // highlight movement grid
                        var neighbors = getNeighbors(board, row, column);
                        for (var i = 0; i < neighbors.length; i++) {
                            // skip out of bounds indexes
                            if (!neighbors[i])
                                continue;
                            evaluateMovement(cell, neighbors[i], board);
                        }

                        currentPlayer.previousSelection = cell;
                    }

                    // if moving animal
                    if (cell.highlight === cellHighlights.moveShadow || cell.highlight === cellHighlights.attackShadow) {
                        // unhighlight animal
                        var previousRow = currentPlayer.previousSelection.animal.x;
                        var previousColumn = currentPlayer.previousSelection.animal.y;
                        unhighlightCell(board, previousRow, previousColumn);
                        // remove enemy animal
                        if (cell.animal)
                            cell.animal.player.animals--;
                        // move animal
                        cell.animal = currentPlayer.previousSelection.animal;
                        cell.animal.x = row;
                        cell.animal.y = column;
                        // remove references
                        currentPlayer.previousSelection.animal = null;
                        currentPlayer.previousSelection = null;

                        moveHasBeenMade = true;
                    }
                },
                isLegalMove: function (board, row, column, currentPlayer) {
                    // TODO should expand this - make some checks
                    return true;
                },
                getClassForCell: function (cell) {
                    return 'end-boardgame-jungle-chess-cell';
                },
                getClassForPlayerIndicator: function () {
                    return 'end-boardgame-jungle-chess-player-indicator';
                },
                moveHasBeenMade: function () {
                    return moveHasBeenMade;
                }
            }
        })
        .directive('endBoardgameJungleChessPlayerIndicator', function factoryFunction() {
            return {
                restrict: 'C',
                link: function (scope, element) {
                    scope.$watch('player.gameName', function (newValue) {
                        element.addClass(newValue);
                    });
                }
            };
        })
        .directive('endBoardgameJungleChessCell', function factoryFunction() {
            return {
                restrict: 'C',
                link: function (scope, element) {
                    element.parent().append('<div class="cell-overlay"></div>');
                    scope.$watch('cell.animal', function (newAnimal) {
                        if (newAnimal) {
                            var htmlString = '<img src="images/boardgames/jungle-chess/animals/' + newAnimal.name + '.png" ' +
                                'class="contentImage ' + newAnimal.player.gameName + '" />';
                            htmlString += '<p class="power-level">' + newAnimal.powerLevel + '</p>';
                            element.html(htmlString);
                        } else {
                            element.html('');
                        }
                    });
                    scope.$watch('cell.type', function (type) {
                        if (type) {
                            element.parent().css('background-image', 'url(images/boardgames/jungle-chess/backgrounds/' + type + '.png)');
                        }
                    });
                    scope.$watch('cell.highlight', function (highlight) {
                        if (highlight) {
                            element.parent().addClass(highlight);
                        } else {
                            for (var highlight in cellHighlights) {
                                element.parent().removeClass(cellHighlights[highlight]);
                            }
                        }
                    })
                }
            };
        });

    function Animal(name, startingX, startingY, powerLevel, player) {
        this.name = name;
        this.x = startingX;
        this.y = startingY;
        this.powerLevel = powerLevel || 0;
        this.player = player;
    }

    Animal.prototype.canSwim = function () {
        if (this.name == 'Rat')
            return true;
        // alternate rule: dog may swim
        if (ruleValues.dogCanSwim && this.name == 'Dog')
            return true;
        return false;
    };

    Animal.prototype.canJump = function (distance) {
        if (this.name == 'Tiger')
            return true;
        if (this.name == 'Lion')
        // alternate rule: lion may not jump horizontally
            if (ruleValues.lionJump)
                return distance > 2;
            else
                return true;
        // alternate rule: leopard may jump horizontally
        if (this.name == 'Leopard' && ruleValues.leopardJump && distance == 3) // TODO check with the single player version: 2 or 3?
            return true;
        return false;
    };

    Animal.prototype.canKill = function (defender) {
        var attacker = this;
        // alternate rule: elephant may not kill the rat
        if (!ruleValues.elephantMayKillRat && attacker.name == 'Elephant' && defender.name == 'Rat')
            return false;
        // a larger animal may attack a smaller or equal one
        if (attacker.powerLevel >= defender.powerLevel) {
            return true;
        }
        // rat may attack elephant
        if (attacker.name == 'Rat' && defender.name == 'Elephant')
            return true;
        // a smaller animal may not attack a larger one
        if (attacker.powerLevel < defender.powerLevel) {
            return false;
        }
        alert("You can't get here");
    };

    var boardMath = {
        lineDistance: function (point1, point2) {
            var xs = point2.x - point1.x;
            xs = xs * xs;

            var ys = point2.y - point1.y;
            ys = ys * ys;

            return Math.sqrt(xs + ys);
        }
    };

})();